package io.github.eric.urpc.core.handler;

import io.github.eric.urpc.core.config.AbstractContext;
import io.github.eric.urpc.core.exception.UrpcMsgDecException;
import io.github.eric.urpc.core.rpc.MessageInfo;
import io.github.eric.urpc.core.rpc.Response;
import io.github.eric.urpc.core.serializer.Serializer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.nio.charset.StandardCharsets;
import java.util.List;

public class ProtoDecoder extends ByteToMessageDecoder {
	private final AbstractContext abstractContext;

	public ProtoDecoder(AbstractContext abstractContext) {
		this.abstractContext = abstractContext;
	}

	@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf in,
	                      List<Object> out) throws Exception {
		try {
			int totalBytes = in.readableBytes();

			//msg class name
			short clzLength = in.readShort();
			byte[] clzBytes = new byte[clzLength];
			in.readBytes(clzBytes);
			String clazzName = new String(clzBytes, StandardCharsets.UTF_8);
			Class<?> clazz = Class.forName(clazzName);

			//int bodyLength
			int payloadLength = totalBytes - 2 - clzLength;
			byte[] payload = new byte[payloadLength];
			in.readBytes(payload);

			if (!Response.class.isAssignableFrom(clazz)) {
				MessageInfo messageInfo = abstractContext.getMessageRegistrar().getMessageInfo(clazzName);
				if (messageInfo == null) {
					if (abstractContext.getLogger().isWarnEnabled()) {
						abstractContext.getLogger().warn("[urpc] receive msg {} from {}, but can not find rpc method, drop it",
								clazzName, ctx.channel().remoteAddress());
					}
					return;
				}
			}
			out.add(Serializer.Json.getSerializer().deserialize(payload, clazz));
		} catch (Exception e) {
			in.resetReaderIndex();
			abstractContext.getLogger().error("[urpc] message decode error from {}, drop it",
					ctx.channel().remoteAddress(), e);
			throw new UrpcMsgDecException(e);
		}
	}

	public ProtoDecoder copy() {
		return new ProtoDecoder(abstractContext);
	}
}
